home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Utilities / Crawl.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-28  |  29.6 KB  |  1,099 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        Crawl.cpp
  3.  
  4.     Contains:    Debugging utility to create and interpret stack crawls
  5.  
  6.     Owned by:    Jens Alfke (based on code from the Macintosh Debugger.)
  7.  
  8.     Copyright:    © 1992 - 1996 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.          <4>     6/23/96    TJ        1344349: Need define for MrC.
  13.          <3>     5/24/96    jpa        Fixed comments
  14.          <2>     5/24/96    jpa        1318444: Supports CW 68K and CW9 PPC. Fixed
  15.                                     crashers in New & GetMem.
  16.     In Progress:
  17.         
  18. */
  19.  
  20.  
  21. #ifndef _CRAWL_
  22. #include "Crawl.h"
  23. #endif
  24.  
  25. #ifndef __LOWMEM__
  26. #include <LowMem.h>
  27. #endif
  28.  
  29. #ifndef __MEMORY__
  30. #include <Memory.h>
  31. #endif
  32.  
  33. #ifndef __STRING__
  34. #include <string.h>
  35. #endif
  36.  
  37. #ifndef __STDARG__
  38. #include <stdarg.h>
  39. #endif
  40.  
  41. #ifndef __ERRORS__
  42. #include <Errors.h>
  43. #endif
  44.  
  45. #include <setjmp.h>
  46. #include <stdio.h>
  47. #include <Unmangler.h>
  48.  
  49.  
  50. //==============================================================================
  51. // Declarations for embedded-symbol stuff
  52. //==============================================================================
  53.  
  54. typedef unsigned long    ULongWord;
  55. typedef unsigned short    UWord;
  56. typedef unsigned char    UByte;
  57.  
  58. typedef size_t            TargetAddress;
  59.  
  60. typedef OSErr (*ReadMemFn)( void *loc, ULongWord size, void *buffer, va_list args );
  61.  
  62. #define kNoErr            noErr
  63. #define kSymbolNotFound fragSymbolNotFound
  64.  
  65.  
  66. #ifdef GENERATINGPOWERPC
  67. static OSErr LookupPowerPCSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  68.         ReadMemFn readMemCallback, ...);
  69. #endif
  70. static OSErr Lookup68KSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  71.         ReadMemFn readMemCallback, ...);
  72.  
  73. #define PACKET_MAX_DATA_LENGTH    512
  74.  
  75.  
  76. //==============================================================================
  77. // Stack-crawl types & constants
  78. //==============================================================================
  79.  
  80. struct LinkAreaPPC {
  81.     void*    backChain;
  82.     void*    savedCR;
  83.     void*    savedLR;
  84.     void*    reserved;
  85.     void*    savedTOC;
  86. };
  87.  
  88. struct LinkArea68k {
  89.     void*    backChain;
  90.     void*    returnAddress;
  91. };
  92.  
  93. union LinkArea {
  94.     LinkAreaPPC fPPC;
  95.     LinkArea68k f68k;
  96. };
  97.  
  98. const size_t kMagicA6 = 0xFFFFFFFF;                // Signals 68k->PPC switch
  99. const size_t kPPCInstrLen = 4;                    // Gotta love them RISCs
  100.  
  101.  
  102. //==============================================================================
  103. // Locating Stack Pointer
  104. //==============================================================================
  105.  
  106. #if GENERATING68K
  107.     static void* GetA6( ) = {0x200E}; // MOVE.L A6,D0
  108. #elif GENERATINGPOWERPC
  109.     #ifdef __MWERKS__
  110.         #if __MWERKS__ >= 8
  111.             asm static void* GetSP( ) {
  112.                 mr r3,SP
  113.                 blr
  114.             }
  115.         #else
  116.             const long kjmp_bufStackFrameIndex = 3;            // For CW8 and earlier, not CW9
  117.         #endif
  118.     #elif defined(__MRC__)
  119.         const long kjmp_bufStackFrameIndex = 2;            // For MRC
  120.     #else
  121.         #error "Don't know offset of SP in jmp_buf for this compiler"
  122.     #endif
  123. #else
  124.     #error "What the hell kinda CPU is this?"
  125. #endif
  126.  
  127. //==============================================================================
  128. // StackCrawl
  129. //==============================================================================
  130.  
  131. StackCrawl*
  132. StackCrawl::New( long startAt, long endAt )
  133. {
  134.     StackCrawl *stackCrawl = NULL;
  135.     const void *stackTop;
  136.     
  137.     // Skanky ways to read the CPU registers:
  138. #if GENERATING68K
  139.     stackTop = GetA6();
  140. #elif GENERATINGPOWERPC
  141.     #if defined(__MWERKS__) && __MWERKS__ >= 8
  142.         stackTop = GetSP();
  143.     #else
  144.     {
  145.         jmp_buf regs;
  146.         (void) setjmp(regs);
  147.         stackTop = regs[kjmp_bufStackFrameIndex];
  148.     }
  149.     #endif
  150. #endif
  151.     
  152.     // We're going to do this loop twice. The first time just count the number of frames.
  153.     // Then allocate enough memory to store them. Second time, write the frame data:
  154.     for( ;; ) {
  155.     
  156.         const LinkArea *stackFrame = (LinkArea*)stackTop;
  157.         const LinkArea *lastStackFrame = NULL;
  158.         Boolean isNative = GENERATINGPOWERPC ?true :false;
  159.         const void *pc;
  160.         
  161.         // Crawl up the stack:
  162.         long nFrames = 0;
  163.         while( stackFrame!=NULL && stackFrame>lastStackFrame ) {
  164.             const LinkArea *nextStackFrame;
  165.             
  166.             if( ! isNative ) {
  167.                 nextStackFrame = (LinkArea*) stackFrame->f68k.backChain;
  168.                 if( ((size_t*)nextStackFrame)[-1] == kMagicA6 ) {        // 68k->PPC switch
  169. #ifdef GENERATINGPOWERPC
  170.                     isNative = true;
  171.                     stackFrame = nextStackFrame;                        // Skip switch frame
  172.                 } else
  173. #else
  174.                     DebugStr("\pOD: PPC frame on 68k machine? Weird!");
  175.                 }
  176. #endif
  177.                     pc = stackFrame->f68k.returnAddress;
  178.             }
  179.  
  180. #ifdef GENERATINGPOWERPC
  181.             if( isNative ) {
  182.                 nextStackFrame = (const LinkArea*) stackFrame->fPPC.backChain;
  183.                 if( (long)nextStackFrame & 1 ) {                    // PPC->68k switch
  184.                     nextStackFrame = (LinkArea*)( (size_t)nextStackFrame -1);
  185.                     isNative = false;
  186.                     pc = nextStackFrame->f68k.returnAddress;
  187.                     nextStackFrame = (const LinkArea*) nextStackFrame->f68k.backChain;
  188.                 } else {
  189.                     pc = (void*)( (size_t)nextStackFrame->fPPC.savedLR - kPPCInstrLen );
  190.                 }
  191.             }
  192. #endif
  193.             
  194.             // Write PC value into list if in write phase:
  195.             if( stackCrawl )
  196.                 if( startAt<=nFrames && nFrames<=endAt )
  197.                     stackCrawl->fFrame[nFrames-startAt] = (const void*)( (size_t)pc | isNative );
  198.             
  199.             // Advance to next frame:
  200.             lastStackFrame = stackFrame;
  201.             stackFrame = (LinkArea*)nextStackFrame;
  202.             nFrames++;
  203.             
  204.             // Stop if we've hit the base of the stack:
  205.             size_t postFrame = (size_t)stackFrame + (isNative ?sizeof(LinkAreaPPC)
  206.                                                               :sizeof(LinkArea68k));
  207.             if( postFrame >= (size_t)LMGetCurStackBase() )
  208.                 break;
  209.         }
  210.         
  211.         if( stackCrawl )
  212.             break;    // Done
  213.         else {
  214.             // We've counted the frames, now allocate storage and go round again:
  215.             // Adjust startAt/endAt:
  216.             if( startAt < 0 )
  217.                 startAt = 0;
  218.             if( endAt <= 0 )
  219.                 endAt += nFrames-1;
  220.             else if( endAt >= nFrames )
  221.                 endAt = nFrames-1;
  222.             if( endAt<startAt )
  223.                 endAt = startAt;
  224.             // Create the StackCrawl object:
  225.             stackCrawl = new (nFrames) StackCrawl(endAt-startAt+1);
  226.         }
  227.     }
  228.     
  229.     return stackCrawl;
  230. }
  231.  
  232.  
  233. void* StackCrawl::operator new( size_t baseSize, long nFrames )
  234. {
  235.     return ::operator new( baseSize + (offsetof(StackCrawl,fFrame)-sizeof(StackCrawl))
  236.                                     + nFrames * sizeof(void*) );
  237. }
  238.  
  239.  
  240. Boolean
  241. StackCrawl::IsFrameNative( long i ) const
  242. {
  243.     return (long)fFrame[i] & 1;
  244. }
  245.  
  246.  
  247. const void*
  248. StackCrawl::GetFramePC( long i ) const
  249. {
  250.     return (const void*)( (long)fFrame[i] & ~1);
  251. }
  252.  
  253.  
  254. static OSErr GetMem( void *loc, ULongWord size, void *buffer, va_list args )
  255. {
  256.     // Try to get away without a CPU exception handler by sanity checking:
  257.     if( loc<&SystemZone()->heapData || (void*)loc>LMGetBufPtr()
  258.                                     || (void*)((size_t)loc+size)>LMGetBufPtr() )
  259.         return -1;
  260.     else {
  261.         BlockMoveData(loc,buffer,size);
  262.         return noErr;
  263.     }
  264. }
  265.  
  266. Boolean
  267. StackCrawl::LookupSymbol( long i, char fnName[], size_t *offset ) const
  268. {
  269.     size_t pc = (size_t) this->GetFramePC(i);
  270.     size_t begin,end;
  271.     
  272.     OSErr err;
  273.     char symbol[256];
  274.     if( this->IsFrameNative(i) )
  275. #ifdef GENERATINGPOWERPC
  276.         err= LookupPowerPCSym(pc,symbol,&begin,&end, &GetMem);
  277. #else
  278.         err= 12345;
  279. #endif
  280.     else
  281.         err= Lookup68KSym(pc,symbol,&begin,&end, &GetMem);
  282.     *offset = pc-begin;
  283.     if( err==noErr && symbol[0] ) {
  284.         // For some reason the traceback names seem to start with "."
  285.         unmangle(fnName,&symbol[1+(symbol[1]=='.')],255);
  286.         return true;
  287.     } else {
  288.         sprintf(fnName,"%08p (%s)", pc, this->IsFrameNative(i) ?"PPC" :"68k");
  289.         return false;
  290.     }
  291. }
  292.  
  293.  
  294. Boolean
  295. StackCrawl::GetFrameName( long i, char name[] ) const
  296. {
  297.     size_t offset;
  298.     
  299.     if( this->LookupSymbol(i,name,&offset) ) {
  300.         sprintf(name+strlen(name)," +%lx", offset);
  301.         return true;
  302.     } else
  303.         return false;
  304. }
  305.  
  306.  
  307. void
  308. StackCrawl::AsString( char str[], long maxLen, const char delimiter[] ) const
  309. {
  310.     char fnName[256];
  311.     size_t offset;
  312.     long len;
  313.     
  314.     str[0] = '\0';
  315.     for( long i=0; i<fNFrames; i++ ) {
  316.         if( i>0 ) {
  317.             len = strlen(delimiter);
  318.             maxLen -= len;
  319.             if( maxLen < 0 )
  320.                 return;
  321.             strcat(str,delimiter);
  322.             str += len;
  323.         }
  324.         this->LookupSymbol(i,fnName,&offset);
  325.         
  326.         len = strlen(fnName);
  327.         maxLen -= len;
  328.         if( maxLen < 0 )
  329.             return;
  330.         strcat(str,fnName);
  331.         str += len;
  332.     }
  333. }
  334.  
  335.  
  336. Boolean
  337. GetNameOfCaller( char str[] )
  338. {
  339.     StackCrawl *c = StackCrawl::New(2,2);
  340.     Boolean gotName = c->GetFrameName(0,str);
  341.     delete c;
  342.     return gotName;
  343. }
  344.  
  345.  
  346. Boolean
  347. StackCrawl::operator== ( const StackCrawl &sc ) const
  348. {
  349.     if( fNFrames != sc.fNFrames )
  350.         return false;
  351.     else
  352.         return memcmp(fFrame,sc.fFrame, fNFrames*sizeof(void*)) ==0;
  353.             
  354. }
  355.  
  356. unsigned long
  357. StackCrawl::Hash ( ) const
  358. {
  359.     unsigned long hash = 0;
  360.     for (long i = 0; i < fNFrames; i++)
  361.     {
  362.         hash ^= (unsigned long) fFrame[i];
  363.     }
  364.     return hash;
  365. }
  366.  
  367.  
  368. //==============================================================================
  369. // Embedded Symbol Extraction (copied from debugger nub)
  370. //==============================================================================
  371.  
  372.  
  373. // max length of an embedded symbol (longer will be truncated)
  374. #define kMaxNameLength                        256
  375. #define kTTblMaxNameLength                    256
  376. #define kMaxTracebackNameSearchLength        512
  377.  
  378.  
  379.  
  380. // PowerPC embedded symbol stuff:
  381.  
  382. #define kMaxPwrPCFnLength    262144
  383. #define kMaxPwrPCFnInstrs    (kMaxPwrPCFnLength >> 2)
  384.  
  385. typedef struct TracebackTblEndAlt
  386.     {
  387.         ULongWord        unknown;
  388.         ULongWord        fnLength;
  389.         UWord            nameLength;
  390.         char            name[kTTblMaxNameLength];
  391.     } TracebackTblEndAlt;
  392.  
  393. typedef struct TracebackTblEnd
  394.     {
  395.         ULongWord        fnLength;
  396.         UWord            nameLength;
  397.         char            name[kTTblMaxNameLength];
  398.     } TracebackTblEnd;
  399.  
  400. typedef struct TracebackTbl
  401.     {
  402.         ULongWord            zero;
  403.         char                version;
  404.         char                language;
  405.         char                flags[6];
  406.         union {
  407.         TracebackTblEnd        end;    // if flags[4] == 0
  408.         TracebackTblEndAlt    endAlt;    // if flags[4] > 0
  409.         } u;
  410.     } TracebackTbl;
  411.  
  412.  
  413. static TracebackTbl *FindTracebackTbl (void *addr, TracebackTbl *ttbl, ReadMemFn readMemCallback, va_list args);
  414.  
  415.  
  416.  
  417. // 68K embedded symbol stuff:
  418.  
  419. #define kMax68KFnLength        32768
  420.  
  421. #define kLINKA6                0x4E56
  422. #define kJMPA0                0x4ED0
  423. #define kRTS                0x4E75
  424. #define kRTD                0x4E74
  425.  
  426.  
  427.  
  428. static UByte    *GetProcStart (UByte *addressOfFnEnd, ReadMemFn readMemCallback, va_list args);
  429. static UByte    *FindNextModule (UByte *start, UByte *limit, Boolean *hasName, ReadMemFn readMemCallback, va_list args);
  430. static Boolean    ValidName (UByte *name, ReadMemFn readMemCallback, va_list args);
  431. static void        GetModuleName (UByte *address, char *name, UByte **dataStart, UByte **dataEnd, ReadMemFn readMemCallback, va_list args);
  432. static Boolean    FindReturn (UByte *start, UByte *limit, UByte **afterReturn, ReadMemFn readMemCallback, va_list args);
  433. static Boolean    LegalSymbolChar (short ch);
  434. static Boolean    LegalTargetChar (short ch);
  435.  
  436. #pragma segment CodeTracking
  437.  
  438. #ifdef GENERATINGPOWERPC
  439. //
  440. // Tries to find a traceback table for the routine in which addr is presumed to be.
  441. // If successful, returns the name of the routine in name and the addresses of the
  442. // beginning and end of the routine in fnBegin and fnEnd.  Else scans forward for
  443. // a blr instruction and backward for an mflr instruction to guesstimate fnBegin
  444. // and fnEnd, and leaves name empty.
  445. //
  446. OSErr LookupPowerPCSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  447.         ReadMemFn readMemCallback, ...)
  448. {
  449.     va_list            args;
  450.     TracebackTbl    ttbl;
  451.     TracebackTbl    *where;
  452.     char            *namePtr;
  453.     UWord            nameLength;
  454.     ULongWord        codeStart, fnLength;
  455.     ULongWord        instr;
  456.     long            i, offset;
  457.  
  458.     va_start( args, readMemCallback );
  459.     
  460.     #define    kmflrInstr    0x7C0802A6
  461.     #define    kblrInstr    0x4E800020
  462.     
  463.     *fnBegin = addr;
  464.     *fnEnd = addr;
  465.  
  466.     where = FindTracebackTbl ((void*) addr, &ttbl, readMemCallback, args);
  467.  
  468.     if (where)
  469.     {
  470.         fnLength = ttbl.flags[4] ? ttbl.u.endAlt.fnLength : ttbl.u.end.fnLength;
  471.         codeStart = (ULongWord) where - fnLength;
  472.         offset = addr - codeStart;
  473.         if (offset >= 0)
  474.         {
  475.             namePtr = ttbl.flags[4] ? ttbl.u.endAlt.name : ttbl.u.end.name;
  476.             nameLength = ttbl.flags[4] ? ttbl.u.endAlt.nameLength : ttbl.u.end.nameLength;
  477.             if (nameLength > kMaxNameLength - 2)
  478.                 // leave room for leading length byte and terminating null
  479.                 nameLength = kMaxNameLength - 2;
  480.             strncpy (&name[1], namePtr, nameLength);
  481.             name[0] = nameLength;            // now it's a Pascal string
  482.             name[nameLength + 1] = 0;        // terminating null to make it a TProtocolString
  483.             if (nameLength & 1)                // do we need a pad byte?
  484.                 name[nameLength + 2] = 0;    // add one
  485.             *fnBegin = (TargetAddress) codeStart;
  486.             *fnEnd = (TargetAddress) where;
  487.             return kNoErr;
  488.         }
  489.     }
  490.  
  491.     // couldn't find a traceback, so no embedded name, but we'll scan for the fn boundaries anyway
  492.     for (i = 0; i < kMaxPwrPCFnInstrs; ++i)
  493.     {
  494.         if ((*readMemCallback) ((void*) *fnBegin, 4, &instr, args) != kNoErr)
  495.             return kSymbolNotFound;
  496.         if (instr == kmflrInstr)
  497.             break;
  498.         else if (instr == kblrInstr)
  499.             {
  500.             // probably a leaf routine, and we've backed up into the preceding fn
  501.             *fnBegin += 4;
  502.             break;
  503.             }
  504.         *fnBegin -= 4;
  505.     }
  506.     
  507.     if (i == kMaxPwrPCFnInstrs)
  508.     {
  509.         // didn't find the fn begin
  510.         *fnBegin = addr;
  511.         *fnEnd = addr;
  512.         return kSymbolNotFound;
  513.     }
  514.  
  515.     // found the fn begin, now try to find the fn end
  516.     for (i = 0; i < kMaxPwrPCFnInstrs; ++i)
  517.     {
  518.         if ((*readMemCallback) ((void*) *fnEnd, 4, &instr, args) != kNoErr)
  519.             return kSymbolNotFound;
  520.         *fnEnd += 4;
  521.         if (instr == kblrInstr)
  522.             break;
  523.     }
  524.     
  525.     if (i == kMaxPwrPCFnInstrs)
  526.     {
  527.         // didn't find the fn end
  528.         *fnBegin = addr;
  529.         *fnEnd = addr;
  530.         return kSymbolNotFound;
  531.     }
  532.     
  533.     return kNoErr;
  534. }
  535.  
  536.  
  537. //
  538. // Tries to find a traceback table for the routine in which addr is presumed to be.  If
  539. // successful, returns a pointer to the traceback table and copies its contents into ttbl.
  540. // If not successful, returns NULL.
  541. //
  542. static TracebackTbl *FindTracebackTbl (void *addr, TracebackTbl *ttbl, ReadMemFn readMemCallback, va_list args)
  543. {
  544.     ULongWord        where = (ULongWord) addr;
  545.     ULongWord        i, memLongWord, fnLength;
  546.     char            *name;
  547.     UWord            nameLength;
  548.     
  549.     for (i = 0; i < kMaxPwrPCFnLength; i += 4)
  550.     {
  551.         if ((*readMemCallback) ((void *) where, 4, &memLongWord, args) != kNoErr)
  552.             return NULL;
  553.         if (memLongWord == 0)
  554.             break;
  555.         where += 4;
  556.     }
  557.     
  558.     if (i == kMaxPwrPCFnLength)
  559.         return NULL;
  560.  
  561.     if ((*readMemCallback) ((void *) where, sizeof(TracebackTbl), ttbl, args) != kNoErr)
  562.         return NULL;
  563.     
  564.     // now for some sanity checks
  565.     fnLength = ttbl->flags[4] ? ttbl->u.endAlt.fnLength : ttbl->u.end.fnLength;
  566.     name = ttbl->flags[4] ? ttbl->u.endAlt.name : ttbl->u.end.name;
  567.     nameLength = ttbl->flags[4] ? ttbl->u.endAlt.nameLength : ttbl->u.end.nameLength;
  568.     if ((fnLength > kMaxPwrPCFnLength) || (nameLength > kMaxTracebackNameSearchLength) || (name[0] < ' ') || (name[0] > '}'))
  569.         return NULL;
  570.     else
  571.         return (TracebackTbl *) where;
  572. }
  573. #endif /*GENERATINGPOWERPC*/
  574.  
  575. //
  576. // Tries to find a Macsbug symbol for the routine in which addr is presumed to be.
  577. // If successful, returns the name of the routine in name and the addresses of the
  578. // beginning and end of the routine in fnBegin and fnEnd.
  579. // Name is a "PC" string (a Pascal string with a terminating null).
  580. // fnBegin should normally always be less than or equal to addr, but if addr happens
  581. // to be somewhere strange (like in an embedded symbol itself!), we may actually
  582. // find the next function.
  583. //
  584. OSErr Lookup68KSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  585.         ReadMemFn readMemCallback, ...)
  586. {
  587.     va_list            args;
  588.     UByte*            address = (UByte*) addr;
  589.     UByte*            limit = (UByte*) (addr + kMax68KFnLength);
  590.     UByte*            addressOfFnEnd;
  591.     UByte*            codeStart;
  592.     UByte*            ignore1;
  593.     UByte*            ignore2;
  594.     Boolean            hasName;
  595.  
  596.     va_start( args, readMemCallback );
  597.     
  598.     addressOfFnEnd = FindNextModule (address, limit, &hasName, readMemCallback, args);
  599.     
  600.     if (addressOfFnEnd)
  601.         {
  602.         //    10/18/94 dkk
  603.         //    Code here used to search backwards from the end of the function to find the start of this function.
  604.         //    If the start of the function wasn't before the address we were interested in finding the symbol for
  605.         //    then we searched again from the original address. We then would search forward again for the end of
  606.         //    the function. This seemed like a lot of extra work. I changed the code look for the start of the
  607.         //    function from the original start address, and don't look for the end of function twice.
  608.         //    We've found a module for the address, find the start of the procedure.
  609.         //
  610.         codeStart = GetProcStart (address, readMemCallback, args);
  611.  
  612.         if (hasName)
  613.             GetModuleName (addressOfFnEnd, name, &ignore1, &ignore2, readMemCallback, args);
  614.         else
  615.             *name = 0;
  616.             
  617.         *fnBegin = (TargetAddress) codeStart;
  618.         *fnEnd = (TargetAddress) addressOfFnEnd;
  619.         return kNoErr;
  620.         }
  621.     
  622.     *fnBegin = addr;
  623.     *fnEnd = addr + kMax68KFnLength;
  624.     return kSymbolNotFound;
  625. }
  626.  
  627.  
  628. //    
  629. //    GetProcStart
  630. //
  631. //        Search backwards for the start of the procedure and return its address.
  632. //
  633. //        10/18/94 - dkk
  634. //        This code was pulled from MacsBug, which didn't suffer any overhead hit from reading one word of memory
  635. //        at a time. Since when MacsBug is executing, it always has its own bus error handler installed, there
  636. //        is no need to install a special handler for each memory access. For the nubs, this is not the case.
  637. //        Each memory access require installation and removal of the bus error handler. For this reason, this code
  638. //        was changes to read a buffer at a time, and step through the buffer. In addition the check for RTS,
  639. //        JMP (A0), and RTD were moved to here, even though this duplicateds code in FindNextModule. FindNextModule
  640. //        will also do the check, but another memory access is required to do the check. By checking here first,
  641. //        we save the overhead of that memory access most of the time.
  642.  
  643. static UByte *GetProcStart (UByte *addressOfFnEnd, ReadMemFn readMemCallback, va_list args)
  644. {
  645.     UWord        instr;
  646.     UByte        *codeStart;
  647.     UByte        *limit = NULL;
  648.     UByte        *prevDataStart;
  649.     char        prevProcName[kMaxNameLength];
  650.     char        memBuffer[PACKET_MAX_DATA_LENGTH];
  651.     short        offset;
  652.     Boolean        hasName;
  653.  
  654.     if ((ULongWord) addressOfFnEnd >= (ULongWord) kMax68KFnLength)
  655.         limit = addressOfFnEnd - kMax68KFnLength;
  656.  
  657.     codeStart = addressOfFnEnd - 2 - (PACKET_MAX_DATA_LENGTH - 2);
  658.     
  659.     if ((*readMemCallback) ((void *) codeStart, PACKET_MAX_DATA_LENGTH, memBuffer, args) != kNoErr)
  660.         return NULL;
  661.     offset = PACKET_MAX_DATA_LENGTH - 2;
  662.     instr = *(short *) &memBuffer[offset];
  663.  
  664.     //    RTD is a 4 byte instruction. RTS and JMP (A0) are 2 byte instructions.
  665.     //
  666.     if ((instr != kRTS) && (instr != kJMPA0))
  667.         offset -= 2;
  668.  
  669.     //    Step backwards looking for the start of the procedure.
  670.     //
  671.     while (codeStart > limit /* gCurBlock.data */)
  672.         {
  673.         offset -= 2;
  674.  
  675.         if (offset < 0)
  676.         {
  677.             codeStart -= PACKET_MAX_DATA_LENGTH;
  678.             offset = PACKET_MAX_DATA_LENGTH - 2;
  679.             if ((*readMemCallback) ((void *) codeStart, PACKET_MAX_DATA_LENGTH, memBuffer, args) != kNoErr)
  680.                 return NULL;
  681.         }
  682.         instr = *(short *) &memBuffer[offset];
  683.  
  684.         //    LinkA6 starts a procedure.
  685.         //
  686.         if (instr == kLINKA6)
  687.             return (codeStart + offset);
  688.  
  689.         if ((instr == kJMPA0) || (instr == kRTS) || (instr == kRTD))
  690.             {
  691.             addressOfFnEnd = FindNextModule (codeStart + offset, codeStart + offset + 2, &hasName, readMemCallback, args);
  692.             if (addressOfFnEnd)
  693.                 {
  694.                 // Found the previous procedure. Its dataEnd is the same as the codeStart we are looking for.
  695.                 //    10/18/94 dkk
  696.                 //    This used to call GetModuleName all the time. Can save the overhead of determining the module
  697.                 //    name again, by checking hasName returned from FindNextModule.
  698.                 //
  699.                 if (hasName)
  700.                     {
  701.                     GetModuleName (addressOfFnEnd, prevProcName, &prevDataStart, &codeStart, readMemCallback, args);
  702.                     return (codeStart);
  703.                     }
  704.                 else
  705.                     return (addressOfFnEnd);
  706.                 }
  707.             }
  708.         }
  709.     return NULL /* (gCurBlock.data) */;
  710. }
  711.  
  712.  
  713. //
  714. //    FindNextModule
  715. //
  716. //        Search for the next module in the address range start to limit-2.  Module names immediately follow either
  717. //        an RTS, RTD (plus offset), or JMP (A0) instruction.  The three formats for legal module names are described
  718. //        in the header comments for ValidName.  If no module name found, return address of end of module where name
  719. //        would have been.  hasName parameter set to indicate whether a name was found.
  720. //
  721. //        return value    -    address of next module
  722. //
  723.  
  724. static UByte *FindNextModule (UByte *start, UByte *limit, Boolean *hasName, ReadMemFn readMemCallback, va_list args)
  725. {
  726.     UByte *fnEnd;
  727.     UByte *firstFollowingModule = NULL;
  728.  
  729.     while ((start < limit) && FindReturn (start, limit, &fnEnd, readMemCallback, args))
  730.         {
  731.         // After the call to FindReturn, fnEnd contains the address following the return instruction (either an RTS,
  732.         //    RTD, or JMP (A0)).  This will be the address of procedure name, if there is one.
  733.         //
  734.         if (firstFollowingModule == NULL)
  735.             firstFollowingModule = fnEnd;
  736.         
  737.         if (ValidName (fnEnd, readMemCallback, args))
  738.             {
  739.             // Found a module name, return its address.
  740.             //
  741.             *hasName = true;
  742.             return (fnEnd);
  743.             }
  744.         else
  745.             // Wasn't a valid name, keep looking starting after return instruction.
  746.             //
  747.             start = fnEnd;
  748.         }
  749.     
  750.     *hasName = false;
  751.     return firstFollowingModule;
  752. }
  753.  
  754.  
  755. //
  756. //    ValidName
  757. //
  758. //        Checks to see if the name at the pointer one of three valid formats for a module name.
  759. //
  760. //        The three formats are:
  761. //
  762. //            Variable length:    The first byte is in the range $80 to $9F and is a length in the
  763. //                                    range 0 to $1F. The high order bit must be set. A length of 0
  764. //                                    implies the second byte is the actual length in the range $01 
  765. //                                    thru $FF. The length byte(s) and name may be an odd number of
  766. //                                    bytes. However, the data after the name is word aligned.
  767. //            Fixed length 8:        The first byte is in the range $20 to $7F and is an ASCII character.
  768. //                                    The high order bit may be set but is not required to be.
  769. //            Fixed length 16:    The first byte is in the range $20 to $7F and is an ASCII character.
  770. //                                    The high order bit may be set but is not required to be.
  771. //                                    The high order bit in the second byte is required to be set.
  772. //                                    This distinguishes the two types of fixed names.
  773. //
  774.  
  775. static Boolean ValidName (UByte *name, ReadMemFn readMemCallback, va_list args)
  776. {
  777.     char        nameCopy[256];
  778.     UByte        *namePtr = (UByte*) nameCopy;
  779.     UWord        length;
  780.     Boolean        valid;
  781.  
  782.     if ((*readMemCallback) ((void *) name, 256, nameCopy, args) != kNoErr)
  783.         return false;
  784.     
  785.     valid = true;
  786.  
  787.     // The high order bit of the first byte of the name is set for variable length names.
  788.     //
  789.     if (*namePtr & 0x80)
  790.         {
  791.         length = *namePtr++ & 0x7F;
  792.  
  793.         // After clearing the high order bit, the first byte should be in the range of 0-1F, for variable
  794.         //    length names.  If the length is 0, the length is in the next byte.  The next byte should not be 0.
  795.         //
  796.         if (length == 0)
  797.             if (*namePtr)
  798.                 length = *namePtr++;
  799.             else
  800.                 valid = false;
  801.         else
  802.             // Maximum length if in the first byte is 1F.
  803.             //
  804.             if (length > 0x1F)
  805.                 valid = false;
  806.         
  807.         if (valid)
  808.             // So far the name is valid, loop through the name checking for the valid symbol characters 
  809.             //    'a'..'z', 'A'..'Z', '0'..'9', '_', '%', '.' (Variable length names cannot have spaces)
  810.             //    10/18/94 dkk
  811.             //    Added check to see that valid was still true. Used to set valid to whatever the last character
  812.             //    in name used to be.
  813.             //
  814.             for (;(length > 0) && valid; --length)
  815.                 if (valid)
  816.                     valid = LegalSymbolChar ((short) *namePtr++);
  817.         
  818.         }
  819.     else
  820.         {
  821.         //    High order bit in first byte of fixed length name may or may not be set.
  822.         //
  823.         valid = LegalTargetChar ((short) (*namePtr++ & 0x7F));
  824.         if (valid)
  825.  
  826.             {
  827.             // First character was legal, check the high order bit of the second byte to see whether it is a 8 byte or
  828.             // 16 byte fixed length name.
  829.             //
  830.             if (*namePtr & 0x80)
  831.             
  832.                 // 16 byte name.  Check the second character and set the length for the remaining 14 characters.
  833.                 //
  834.                 {
  835.                 valid = LegalTargetChar ((short) (*namePtr++ & 0x7F));
  836.                 length = 14;
  837.                 }
  838.             else
  839.                 // 8 byte name.  Check the second character and set the length for the remaining 6 characters.
  840.                 //
  841.                 {
  842.                 valid = LegalTargetChar ((short) *namePtr++);
  843.                 length = 6;
  844.                 }
  845.             
  846.             //    10/18/94 dkk
  847.             //    Added check to see that valid was still true. Used to set valid to whatever the last character
  848.             //    in name used to be.
  849.             //
  850.             for (;(length > 0) && valid; --length)
  851.                 if (valid)
  852.                     valid = LegalTargetChar ((short) *namePtr++);
  853.             }
  854.         }
  855.     return (valid);
  856. }
  857.     
  858.  
  859. //
  860. //    GetModuleName
  861. //
  862. //   Copy the valid MacsBug module name at address into the name string.
  863. //   Set dataStart to point to the first byte after the name after making sure it is word aligned.
  864. //   Set dataEnd to point to the first byte of the next procedure.
  865. //
  866.  
  867. static void GetModuleName (UByte *address, char *name, UByte **dataStart, UByte **dataEnd, ReadMemFn readMemCallback, va_list args)
  868. {
  869.     UWord        memWord;
  870.     UByte        ch, index;
  871.     char        nameCopy[kMaxNameLength];
  872.     UByte        *namePtr = (UByte*) nameCopy;
  873.  
  874.     *name = 0;
  875.     *dataStart = address;
  876.     *dataEnd = address;
  877.     
  878.     if ((*readMemCallback) ((void *) address, kMaxNameLength, nameCopy, args) != kNoErr)
  879.         return;
  880.  
  881.     // Read first character of name, stripping off most significant bit.
  882.     //
  883.     ch = *namePtr++ & 0x7F;
  884.  
  885.     // Variable length name, if value is 0 thru 1F.
  886.     //
  887.     if (ch < 0x20)
  888.         {
  889.         // The name is the new variable format. This may be a module or a method name.
  890.         //
  891.         if (ch == 0)
  892.             // Second byte is the actual length.
  893.             //
  894.             ch = *namePtr++;
  895.  
  896.         name[0] = 0;
  897.         for (index = 1; index <= ch; ++index)
  898.             // When the name gets over kMaxNameLength, truncate the name and just increment to the end of the name.
  899.             //
  900.             if (index <= kMaxNameLength)
  901.                 name[++name[0]] = *namePtr++;
  902.             else
  903.                 break;
  904.         
  905.         // The name may not be word aligned. Data after the name always starts on a word boundary.
  906.         //    10/18/94 dkk
  907.         //    Added additional 2 character offset to address. In translation from MacsBug my guess is the length
  908.         //    bytes were accidentally omitted. This caused a bug where the last character of the symbol name was
  909.         //    added to the symbol name address as the literal pool size. This caused the returned address range
  910.         //    to not contain the address we were interested in.
  911.         //
  912.         address += ch + 2;
  913.         if ((ULongWord) address % 2)
  914.             ++address;
  915.             
  916.         *dataStart = address;
  917.  
  918.         //    Variable format names are followed by a word which defines the length of the literal pool
  919.         //    after the code of this procedure and before the code of the next procedure. If the word is
  920.         //    odd then assume it is not a length.
  921.         //
  922.         if ((*readMemCallback) ((void *) address, sizeof(UWord), &memWord, args) != kNoErr)
  923.             return;
  924.         if (memWord & 1)
  925.             // Length word is odd, not the length.
  926.             //
  927.             *dataEnd = address;
  928.         else
  929.             // Add length word + length.
  930.             //
  931.             *dataEnd = address + 2 + memWord;
  932.  
  933.         name[name[0] + 1] = 0;            // terminating null to make it a TProtocolString
  934.         if (name[0] & 1)                // do we need a pad byte?
  935.             name[name[0] + 2] = 0;        // add one
  936.         }
  937.     else
  938.         {
  939.         // If the most significant bit of the second character is set, the name is the 16 byte class.method format,
  940.         // other wise its the 8 byte format.
  941.         //
  942.         if (*namePtr & 0x80)
  943.             {
  944.             //    The name is the 16 byte class.method format. Class and method are stored in reverse order in memory.
  945.             // Skip to byte 9 of the name to copy.  Address currently points to byte 2.
  946.             //
  947.             namePtr += 7;
  948.  
  949.             // Initialize length byte.
  950.             //
  951.             name[0] = 0;
  952.             
  953.             // Copy characters in the class.
  954.             //
  955.             for (index = 1; index <= 8; ++index)
  956.  
  957.                 // Strip the spaces used to pad to 8 characters.
  958.                 //
  959.                 if (*namePtr != ' ')
  960.                     name[++name[0]] = *namePtr++;
  961.                 else
  962.                     ++namePtr;
  963.  
  964.             //    Insert the '.' to indicate a method
  965.             //
  966.             name[++name[0]] = '.';
  967.             
  968.             // Reset pointer to beginning of the name.
  969.             //
  970.             namePtr -= 16;
  971.             
  972.             // Copy characters in method
  973.             //
  974.             for (index = 1; index <= 8; ++index)
  975.  
  976.                 // Strip the spaces used to pad to 8 characters.  First two bytes also have most significant bit set.
  977.                 //
  978.                 if (*namePtr & 0x7F != ' ')
  979.                     name[++name[0]] = *namePtr++ & 0x7F;
  980.                 else
  981.                     ++namePtr;
  982.  
  983.             // Skip to byte after end of name.
  984.             //
  985.             address += 16;
  986.             }
  987.         else
  988.             {
  989.             // 8 byte format.  Copy first character and initialize length byte.
  990.             //
  991.             name[1] = ch;
  992.             name[0] = 1;
  993.             
  994.             // Copy remaining characters in name.
  995.             //
  996.             for (index = 2; index <= 8; ++index)
  997.  
  998.                 // Strip the spaces used to pad to 8 characters.
  999.                 //
  1000.                 if (*namePtr != ' ')
  1001.                     name[++name[0]] = *namePtr++;
  1002.                 else
  1003.                     ++namePtr;
  1004.  
  1005.             // Skip to byte after end of name.
  1006.             //
  1007.             address += 8;
  1008.             }
  1009.  
  1010.         //    Fixed length names do not indicate the data length. Assumed immediately after the name.
  1011.         //
  1012.         *dataStart = address;
  1013.         *dataEnd = address;
  1014.  
  1015.         name[name[0] + 1] = 0;            // terminating null to make it a TProtocolString
  1016.         if (name[0] & 1)                // do we need a pad byte?
  1017.             name[name[0] + 2] = 0;        // add one
  1018.         }
  1019.     
  1020. }
  1021.  
  1022.  
  1023. //
  1024. //    FindReturn
  1025. //
  1026. //        Search for an RTS, RTD or JMP (A0) instruction.  The address following the instruction is returned in afterReturn.
  1027. //        Function value is true if return is found, false otherwise.
  1028. //
  1029. //        start                -    Address to start search for return.
  1030. //        limit                -    Address to stop search for return.
  1031. //        afterReturn        -    Address after return instruction (if found).
  1032. //        return value    -    True if return is found, false otherwise.
  1033. //
  1034.  
  1035. static Boolean FindReturn (UByte *start, UByte *limit, UByte **afterReturn, ReadMemFn readMemCallback, va_list args)
  1036. {
  1037.     UWord        memWord;
  1038.     
  1039.     while (start < limit)
  1040.         {
  1041.         if ((*readMemCallback) ((void *) start, sizeof(UWord), &memWord, args) != kNoErr)
  1042.             return false;
  1043.         
  1044.         switch (memWord)
  1045.             {
  1046.             case kJMPA0:
  1047.             case kRTS:
  1048.                 *afterReturn = start + 2;            // JMP (A0) and RTS instruction are both two byte instructions
  1049.                 return (true);
  1050.                 break;
  1051.     
  1052.             case kRTD:
  1053.                 *afterReturn = start + 4;            //    RTD is a four byte instruction.
  1054.                 return (true);
  1055.                 break;
  1056.     
  1057.             default:
  1058.                 start += 2;
  1059.                 break;
  1060.             }
  1061.         }
  1062.         
  1063.     return (false);
  1064. }
  1065.  
  1066.  
  1067. //
  1068. //    LegalSymbolChar
  1069. //
  1070. //        Return True if ch is in the set ['a'..'z', 'A'..'Z', '0'..'9', '_', '%', '.']
  1071. //
  1072.  
  1073. static Boolean LegalSymbolChar (short ch)
  1074. {
  1075.     return (((ch >= 'a') && (ch <= 'z')) ||
  1076.                 ((ch >= 'A') && (ch <= 'Z')) ||
  1077.                  ((ch >= '0') && (ch <= '9')) ||
  1078.                   (ch == '_') || (ch == '%') || (ch == '.'));
  1079. }
  1080.  
  1081.  
  1082. //
  1083. //    LegalTargetChar
  1084. //
  1085. //        Return True if ch is in the set ['a'..'z', 'A'..'Z', '0'..'9', '_', '%', '.', ' '].  Same as LegalSymbolChar
  1086. //        except that a space is also a legal character.
  1087. //
  1088.  
  1089. static Boolean LegalTargetChar (short ch)
  1090. {
  1091.     return (((ch >= 'a') && (ch <= 'z')) ||
  1092.                 ((ch >= 'A') && (ch <= 'Z')) ||
  1093.                  ((ch >= '0') && (ch <= '9')) ||
  1094.                   (ch == '_') || (ch == '%') || (ch == '.') || (ch == ' '));
  1095. }
  1096.  
  1097.  
  1098.  
  1099.